﻿using System;
using System.IO;
using System.Linq;
using System.Text;

namespace Job.Core.Security
{
    /// <summary>
    /// 程序授权验证实现(静态类)。
    /// 授权格式：授权ID|授权ID|授权ID$到期日期。
    /// 格式说明：使用$号分格授权ID与到期日期，多授权ID时使用|号分格，授权字串中不能含有$符号。
    /// </summary>
    public static class AppAuthorize
    {
        #region Properties

        private const char dateSplitChar = '$';
        private const char idSplitChar = '|';
        private static readonly bool isWeb = !string.IsNullOrEmpty(AppDomain.CurrentDomain.SetupInformation.DynamicBase);

        #endregion
       
        #region Methods

        /// <summary>
        /// 授权验证。
        /// </summary>
        /// <param name="data">授权数据(可以为web域名等)。</param>
        /// <param name="keyFile">密钥文件路径。</param>
        /// <param name="expire">out 授权过期日期。</param>
        /// <returns>验证授权成功返回true，失败返回false。</returns>
        public static bool IsAuthorized(string data, string keyFile, out DateTime expire)
        {
            expire = DateTime.MinValue;

            if (string.IsNullOrEmpty(keyFile) || !File.Exists(keyFile))
                return false;           

            try
            {
                string key;
                DecryptKeyFile(keyFile, out key);

                string[] keySplit = key.Split(dateSplitChar);
                if (keySplit.Length != 2)
                    return false;

                if (DateTime.TryParse(keySplit[1], out expire) && DateTime.Now < expire)
                {
                    string[] strSplit = keySplit[0].Split(idSplitChar);

                    foreach (string s in strSplit)
                    {
                        if (string.Compare(s, data, true) == 0)
                        {
                            return true;
                        }
                    }
                }

                return false;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 生成密钥文件。
        /// </summary>
        /// <param name="data">授权数据(可以为web域名等)。</param>
        /// <param name="keyFile">密钥文件路径。</param>
        /// <param name="expire">授权过期日期。</param>
        /// <param name="modulus">表示 RSA 算法的 Modulus 参数。</param>
        /// <param name="e">表示 RSA 算法的 E 参数。</param>
        /// <param name="d">表示 RSA 算法的 D 参数。</param>
        public static void CreateKeyFile(string data, string keyFile, DateTime expire, string modulus, string e, string d)
        {
            if (string.IsNullOrEmpty(data))
                throw new ArgumentNullException("data");

            if (data.Contains(dateSplitChar))
                throw new ArgumentException(string.Format("'data' 不能包含 '{0}'", dateSplitChar));

            if (string.IsNullOrEmpty(keyFile))
                throw new ArgumentNullException("keyFile");

            data += dateSplitChar;
            data += expire.ToShortDateString();

            if (Encoding.Default.GetBytes(data).Length > 128)
                throw new ArgumentException("'data' 长度不能大于 '128'");

            Rsa rsa = null;
            StreamWriter sw = null;

            try
            {
                rsa = new Rsa(modulus);
                string cipher = rsa.Encrypt(data, d);
                sw = new StreamWriter(keyFile);
                sw.WriteLine(e);
                sw.WriteLine(modulus);
                sw.WriteLine(cipher);
            }
            finally
            {
                if (sw != null) sw.Dispose();
                if (rsa != null) rsa.Clear();
            }
        }

        /// <summary>
        /// 对密钥文件进行解密。
        /// </summary>
        /// <param name="keyFile">密钥文件路径。</param>
        /// <param name="data">out 解密后的明文。</param>
        public static void DecryptKeyFile(string keyFile, out string data)
        {
            data = string.Empty;

            if (!File.Exists(keyFile))
                throw new FileNotFoundException(string.Empty, "keyFile");

            Rsa rsa = null;
            StreamReader sr = null;

            try
            {
                string modulus, e, cipher;
                sr = new StreamReader(keyFile);
                e = sr.ReadLine();
                modulus = sr.ReadLine();
                cipher = sr.ReadLine();

                rsa = new Rsa(modulus);
                data = rsa.Decrypt(cipher, e);
            }
            catch { }
            finally
            {
                if (sr != null) sr.Dispose();
                if (rsa != null) rsa.Clear();
            }
        }

        #endregion
    }
}